<?php

namespace ASW\Utility;


use JetBrains\PhpStorm\ArrayShape;
use Stringable;

/**
 * 操作结果通用类
 * @template T
 */
class ExecuteResult implements Stringable
{
    /**-----------------------------**/
    /**  Static Region              **/
    /**-----------------------------**/

    public static function fromJson(string $json): ?static
    {
        if (null === $arr = json_decode($json, true)) return null;
        return self::fromArray($arr);
    }

    public static function fromArray(array $arr): static
    {
        $state   = boolval($arr['state'] ?? $arr['success'] ?? false);  // 优先使用 state 字段, // 为了兼容旧版 其次使用 success 字段
        $message = strval($arr['message'] ?? '');
        $data    = $arr['data'] ?? null;
        $code    = intval($arr['code']) ?? 0;
        return new static($state, $message, $data, $code);
    }

    public static function success(mixed $data = null, string $message = 'success', int $code = 0): static
    {
        return new static(true, $message, $data, $code);
    }

    public static function fail(string $message = 'fail', mixed $data = null, int $code = -1): static
    {
        return new static(false, $message, $data, $code);
    }

    /**-----------------------------**/
    /**  Instance Region            **/
    /**-----------------------------**/

    /**
     * @var bool 处理结果是否成功
     */
    public bool $state;

    /**
     * @var string 处理结果的文本信息, 通常用来传递错误文本
     */
    public string $message;

    /**
     * @var string 处理结果的内部文本信息, 通常用来传递不交付给用户的内部信息, 例如用于记录详细日志
     */
    public string $innerMessage = '';

    /**
     * @var T 处理结果数据
     */
    public mixed $data;

    /**
     * @var int 状态码, 默认为0, 一般用负数表达错误码, 正整数表达状态码
     */
    public int $code = 0;

    /**
     * @var array 额外数据
     */
    private array $extra = [];

    /**
     * @param bool $state
     * @param string $message
     * @param T|null $data
     * @param int $code
     */
    protected function __construct(bool $state, string $message = '', mixed $data = null, int $code = 0)
    {
        $this->state   = $state;
        $this->message = $message;
        $this->data    = $data;
        $this->code    = $code;
    }

    /**
     * 设置 message 值
     * @param string $message
     * @param bool $append
     * @return $this
     */
    public function setMessage(string $message, bool $append = false): static
    {
        $this->message = $append ? "$this->message$message" : $message;
        return $this;
    }

    /**
     * 设置 innerMessage 值
     * @param string $innerMessage
     * @return $this
     */
    public function setInnerMessage(string $innerMessage): static
    {
        $this->innerMessage = $innerMessage;
        return $this;
    }

    /**
     * 设置 data 值
     * @param T $data
     * @return $this
     */
    public function setData(mixed $data): static
    {
        $this->data = $data;
        return $this;
    }

    /**
     * 设置 code 值
     * @param int $code
     * @return $this
     */
    public function setCode(int $code): static
    {
        $this->code = $code;
        return $this;
    }

    /**
     * 添加额外数据
     * @param string $key
     * @param mixed $value
     * @return $this
     */
    public function addExtra(string $key, mixed $value): static
    {
        $this->extra[$key] = $value;
        return $this;
    }

    /**
     * 删除额外数据
     * @param string $key
     * @return $this
     */
    public function removeExtra(string $key): static
    {
        unset($this->extra[$key]);
        return $this;
    }

    /**
     * 清空额外数据
     * @return $this
     */
    public function clearExtra(): static
    {
        $this->extra = [];
        return $this;
    }

    /**
     * 重写 __toString, 返回实例的 JSON 文本形式
     * @return string
     */
    public function __toString(): string
    {
        return $this->toJson();
    }

    /**
     * 返回实例的JSON文本形式
     * @param bool $withInnerMessage 是否包含 InnerMessage
     * @return string
     */
    public function toJson(bool $withInnerMessage = false): string
    {
        return json_encode($this->toArray($withInnerMessage), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_BIGINT_AS_STRING);
    }

    /**
     * 返回实例的数组形式
     * @param bool $withInnerMessage 是否包含 InnerMessage
     * @return array
     */
    #[ArrayShape(['state' => "bool", 'message' => "string", 'data' => "mixed", 'code' => "int", 'innerMessage' => "string"])]
    public function toArray(bool $withInnerMessage = false): array
    {
        $array = [
            'state'   => $this->state,
            'success' => $this->state,  // 为了兼容旧版, 保留 success 字段
            'message' => $this->message,
            'data'    => $this->data,
            'code'    => $this->code,
            'extra'   => $this->extra,
        ];
        if ($withInnerMessage) $array['innerMessage'] = $this->innerMessage;
        return $array;
    }

    /**
     * 重写 __invoke, 返回实例是否成功
     * @return bool
     */
    public function __invoke(): bool
    {
        return $this->state;
    }
}